use std::fs;
use crate::utils::{CmdUtil, PLUGIN_CONFIG_NAME, PLUGIN_DIR_NAME};
use crate::{success, APP_DIR, WORK_DIR};
use anyhow::{anyhow, Result};
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use strum::{Display, EnumIter, EnumString};
use crate::service::AppData;

#[derive(Serialize, Deserialize, Clone, Debug)]
struct PluginConfig {
    pub name: String,
    pub desc: String,
    pub exec_type: PluginScript,
    pub exec: String,
}

#[derive(Serialize, Deserialize, Clone, Debug, EnumString, Display, EnumIter)]
pub enum PluginScript {
    #[strum(serialize = "py")]
    Py,
    #[strum(serialize = "lua")]
    Lua,
    #[strum(serialize = "exec")]
    Exec,
}

pub struct Plugin {
    base_path: PathBuf,
    cmd: CmdUtil,
}
impl Plugin {
    pub fn new() -> Result<Self> {
        let (data_path, cmd) = Self::check_dir()?;
        Ok(Plugin { base_path: data_path, cmd })
    }
    pub fn check_dir() -> Result<(PathBuf, CmdUtil)> {
        let mut cmd = CmdUtil::new("".to_string(), Default::default(), Default::default())?;
        cmd.set_debug(false);
        let home_path = APP_DIR.get().expect("获取应用数据目录失败！").clone();
        let plugin_path = Path::new(&home_path).join(PLUGIN_DIR_NAME);
        cmd.current_dir = APP_DIR.get().expect("获取应用数据目录失败！").clone();
        if !plugin_path.exists() {
            std::fs::create_dir(PLUGIN_DIR_NAME)?;
            cmd.exec(&format!("uv venv -p 3.12 {}/.venv", PLUGIN_DIR_NAME))?;
        } else {
            if !plugin_path.join(".venv").exists() {
                cmd.exec(&format!("uv venv -p 3.12 {}/.venv", PLUGIN_DIR_NAME))?;
            }
        }
        Ok((plugin_path, cmd))
    }
    pub fn register(&mut self, plugin: &str, plugin_type: PluginScript, desc: &str, exec: &str, requirement: usize) -> Result<()> {
        let plugin_path = self.base_path.join(plugin);
        if plugin_path.exists() {
            panic!("插件以存在！");
        }
        fs::create_dir(plugin_path.clone())?;
        let config_path = plugin_path.join(PLUGIN_CONFIG_NAME);
        let config = PluginConfig {
            name: plugin.to_string(),
            desc: desc.to_string(),
            exec_type: plugin_type.clone(),
            exec: match exec.is_empty() {
                false => exec.to_string(),
                true => "run.py".to_string()
            },
        };
        let mut fs = OpenOptions::new().write(true).create(true).truncate(true).open(config_path)?;
        let toml_str = toml::to_string(&config)?;
        fs.write_all(toml_str.as_bytes())?;
        match plugin_type {
            PluginScript::Py => {
                let mut fs = OpenOptions::new().create_new(true).write(true).open(plugin_path.join("run.py"))?;
                fs.write_all(b"print('hello world!')")?;
                fs.flush()?;
                if requirement == 0 {
                    fs::File::create(plugin_path.join("requirements.txt"))?;
                }
            }
            PluginScript::Lua => {}
            _ => {}
        }
        Ok(())
    }
    pub fn list_plugins(&self) -> Result<()> {
        let dirs = std::fs::read_dir(self.base_path.clone())?;
        let plugins: Vec<String> = dirs.map(|it| it.unwrap().file_name().to_string_lossy().to_string()).collect();
        for item in plugins {
            if !item.eq(".venv") {
                let plugin = self.read_plugin(&item)?;
                success(format!("{} - {}", plugin.name, plugin.desc));
            }
        }
        Ok(())
    }
    pub fn remove(&self, plugin: &String) -> Result<()> {
        let plugin_path = self.base_path.join(plugin);
        if plugin_path.exists() {
            std::fs::remove_dir_all(plugin_path)?;
        }
        Ok(())
    }
    pub fn run(&mut self, plugin: &String, args: Vec<String>) -> Result<()> {
        let plugin_path = self.base_path.join(plugin);
        let plugin_config = self.read_plugin(plugin)?;
        self.cmd.current_dir = plugin_path.to_string_lossy().to_string();
        let cmd_str = match plugin_config.exec_type {
            PluginScript::Py => {
                if plugin_path.join("requirements.txt").exists() {
                    format!("uv run --no-project --with-requirements requirements.txt {} {}", plugin_config.exec, args.join(" "))
                } else {
                    format!("uv run --no-project {} {}", plugin_config.exec, args.join(" "))
                }
            },
            PluginScript::Lua => "".to_string(),
            PluginScript::Exec => format!("./{} {}", plugin_config.exec, args.join(" "))
        };
        self.cmd.add_env("WORK_DIR".to_string(), WORK_DIR.get().expect("获取当前目录失败！").to_string())?;
        let app = AppData::read_data()?;
        self.cmd.add_env("SERVER_DATA".to_string(), format!("{}", app))?;
        self.cmd.exec(cmd_str.as_str())?;
        Ok(())
    }
    fn read_plugin(&self, plugin: &String) -> Result<PluginConfig> {
        let plugin_path = self.base_path.join(plugin).join(PLUGIN_CONFIG_NAME);
        if !plugin_path.exists() {
            return Err(anyhow!("插件 {} 不存在！", plugin));
        }
        let mut fs = OpenOptions::new().read(true).open(plugin_path)?;
        let mut content = String::new();
        fs.read_to_string(&mut content)?;
        let config: PluginConfig = toml::from_str(&content)?;
        Ok(config)
    }
}